;/////////////////////////////////////////////////////////////;
;                                                             ;
;                 SimpleDX11 Shader Procedures                 ;
;                                                             ;
;                                                             ;
;                   (c) 2013 SimpleDX Team                    ;
;                                                             ;
;                         License: MIT                        ;
;                                                             ;
;/////////////////////////////////////////////////////////////;


;-//    Private structures    //

Structure Shader_Template 
  *vTable
  *p.Shader 
 
  *SD3D.Screen3D
  *Compiler.Compiler
  *Device.ID3D11Device
  *ImmediateContext.ID3D11DeviceContext 
  
  vShader.ID3D11VertexShader
  pShader.ID3D11PixelShader
  
  Shader.ID3D11VertexShader
  Layout.ID3D11InputLayout
  SampleState.ID3D11SamplerState
  
  LightBufferId.l
  CameraBufferId.l
  *Texture
  Type.l  
  
  Array ConstantBuffer.ID3D11Buffer(0)
  
  cbSize.l  
  DefBufferId.l
  
EndStructure

;-//    Constructor    //
Procedure New_Shader(*SD3D.Screen3D, Type.l)
  Protected *this.Shader_Template
 
  *this = AllocateMemory(SizeOf(Shader_Template))
  InitializeStructure(*this, Shader_Template)
  
  If *this
    *this\vTable            = ?Shader_Class    
    *this\p                 = *this
    *this\SD3D              = *SD3D
    
    *this\Device            = *SD3D\GetDevice()
    *this\ImmediateContext  = *SD3D\GetDeviceContext()   
    *this\Compiler          = New_Compiler(*SD3D)
    *this\LightBufferId     = -1
    *this\CameraBufferId    = -1
    *this\Type              = Type
    

    
    ProcedureReturn *this
  EndIf  
  ProcedureReturn #False  
EndProcedure

Declare Shader_CreateShader(*this.Shader_Template)


Procedure Shader_AddTexture(*this.Shader_Template, *Texture)
  Protected SamplerDesc.D3D11_SAMPLER_DESC
  SamplerDesc\Filter          = #D3D11_FILTER_MIN_MAG_MIP_LINEAR
  SamplerDesc\AddressU        = #D3D11_TEXTURE_ADDRESS_WRAP
  SamplerDesc\AddressV        = #D3D11_TEXTURE_ADDRESS_WRAP
  SamplerDesc\AddressW        = #D3D11_TEXTURE_ADDRESS_WRAP
  SamplerDesc\MipLODBias      = 0.0
  SamplerDesc\MaxAnisotropy   = 1
  SamplerDesc\ComparisonFunc  = #D3D11_COMPARISON_ALWAYS
  SamplerDesc\BorderColor[0]  = 0
  SamplerDesc\BorderColor[1]  = 0
  SamplerDesc\BorderColor[2]  = 0
  SamplerDesc\BorderColor[3]  = 0
  SamplerDesc\MinLOD          = 0
  SamplerDesc\MaxLOD          = #D3D11_FLOAT32_MAX

  If *this\Device\CreateSamplerState(SamplerDesc, @*this\SampleState)
   ProcedureReturn #False
  EndIf  

  *this\Texture = *Texture

  ProcedureReturn #True
EndProcedure

Procedure Shader_AddLighting(*this.Shader_Template)
  
  Select *this\Type
    Case 1:       
        CameraBufferDesc.D3D11_BUFFER_DESC
      	CameraBufferDesc\Usage                = #D3D11_USAGE_DYNAMIC
      	CameraBufferDesc\ByteWidth            = SizeOf(CameraConstantBuffer)
      	CameraBufferDesc\BindFlags            = #D3D11_BIND_CONSTANT_BUFFER
      	CameraBufferDesc\CPUAccessFlags       = #D3D11_CPU_ACCESS_WRITE
      	CameraBufferDesc\MiscFlags            = 0
      	CameraBufferDesc\StructureByteStride  = 0  
      	*this\CameraBufferId = *this\p\AddConstantBuffer(CameraBufferDesc)
      Case 2:        
        LightBufferDesc.D3D11_BUFFER_DESC
        LightBufferDesc\Usage           = #D3D11_USAGE_DYNAMIC
        LightBufferDesc\ByteWidth       = SizeOf(LightConstantBuffer)
        LightBufferDesc\BindFlags       = #D3D11_BIND_CONSTANT_BUFFER
        LightBufferDesc\CPUAccessFlags  = #D3D11_CPU_ACCESS_WRITE
        *this\LightBufferId = *this\p\AddConstantBuffer(LightBufferDesc)
  EndSelect
    
EndProcedure




;-//    Public procedures    //

; Type = 1 = VertexShader 2 = PixelShader
Procedure Shader_CompileShaderFile(*this.Shader_Template, File.s, EntryPoint.s, SV.s)
  Protected Result.l = #S_OK
  
  Result = *this\Compiler\ComileShaderFromFile(File, EntryPoint, SV)
  If Result
    MessageRequester("Error", PeekS(*this\Compiler\GetErrorBlobPointer(), *this\Compiler\GetErrorBlobSize(), #PB_Ascii))
    ProcedureReturn *this\Compiler\GetErrorBlobPointer()
  EndIf  
  
  *this\Compiler\SetType(Type) 

  Result = Shader_CreateShader(*this)
  
  ProcedureReturn Result
EndProcedure

; Add a constantbuffer to the shader
Procedure Shader_AddConstantBuffer(*this.Shader_Template, *CBDesc)
  Protected ID.l = *this\cbSize, Result.l = #S_OK
   
  ReDim *this\ConstantBuffer(ID)
  *this\cbSize + 1

  Result = *this\Device\CreateBuffer(*CBDesc, #Null, @*this\ConstantBuffer(ID))
  
  If Result : ProcedureReturn -1 : EndIf
  ProcedureReturn ID 
EndProcedure


; Updates the ConstantBuffer
Procedure Shader_UpdateConstantBuffer(*this.Shader_Template, StartSlot.l, NumBuffers.l, *cbData = #Null)
  If *cbData : *this\ImmediateContext\UpdateSubresource(*this\ConstantBuffer(StartSlot), 0, #Null, *cbData, 0, 0) : EndIf

  Select *this\Type
    Case 1:
      *this\ImmediateContext\VSSetConstantBuffers(StartSlot, NumBuffers, @*this\ConstantBuffer(StartSlot))
    Case 2:
      *this\ImmediateContext\PSSetConstantBuffers(StartSlot, NumBuffers, @*this\ConstantBuffer(StartSlot))
  EndSelect

EndProcedure

Procedure Shader_SetShaderData(*this.Shader_Template, *DataDesc.Shader_DataDescription)   
 
  Select *this\Type
    Case 1:
      
        Protected WorldMatrix.D3DMATRIX
        Protected ViewMatrix.D3DMATRIX 
        Protected ProjectionMatrix.D3DMATRIX        
        
        D3DXMatrixTranspose(WorldMatrix, *DataDesc\WorldMatrix )   
        D3DXMatrixTranspose(ViewMatrix,  *DataDesc\Camera\GetViewMatrix())  
        D3DXMatrixTranspose(ProjectionMatrix, *DataDesc\ProjectionMatrix)       	
      
      	Protected constBuff.D3DMATRIX3 
        constBuff\_1 = WorldMatrix
        constBuff\_2 = ViewMatrix
        constBuff\_3 = ProjectionMatrix	   
        
        *this\p\UpdateConstantBuffer(*this\DefBufferId, 1, constBuff)
        
         If *this\CameraBufferId > -1
           CMP.D3D11_MAPPED_SUBRESOURCE
             	    
           Result = *this\ImmediateContext\Map(*this\ConstantBuffer(*this\CameraBufferId), 0, #D3D11_MAP_WRITE_DISCARD, 0, @CMP)
           If Result
             ProcedureReturn Result
           EndIf
           
           Protected *Pos.XMFLOAT3 = *DataDesc\Camera\GetPosition()
           Protected *cDataPtr.XMFLOAT3 = CMP\pData
           *cDataPtr\x = *Pos\x
           *cDataPtr\y = *Pos\y
           *cDataPtr\z = *Pos\z
           
           *this\ImmediateContext\Unmap(*this\ConstantBuffer(*this\CameraBufferId), 0)
           *this\p\UpdateConstantBuffer(*this\CameraBufferId, 1)
         EndIf 
        
        
      Case 2:
         
       If *this\Texture
          *this\ImmediateContext\PSSetShaderResources(0, 1, @*this\Texture)
       EndIf       
        
       If *this\LightBufferId > -1
         
          Protected *SpecularColor.D3DXCOLOR = *DataDesc\Light\GetSpecularColor()
          Protected *AmbientColor.D3DXCOLOR  = *DataDesc\Light\GetAmbientColor()
          Protected *DiffuseColor.D3DXCOLOR  = *DataDesc\Light\GetDiffuseColor()
          Protected *Direction.XMFLOAT3      = *DataDesc\Light\GetDirection()

          LMP.D3D11_MAPPED_SUBRESOURCE

          Result = *this\ImmediateContext\Map(*this\ConstantBuffer(*this\LightBufferId), 0, #D3D11_MAP_WRITE_DISCARD, 0, @LMP)
          If Result
            ProcedureReturn Result
          EndIf

          Protected *dataPtr.LightConstantBuffer = LMP\pData
          *dataPtr\AmbientColor\r   = *AmbientColor\r
          *dataPtr\AmbientColor\g   = *AmbientColor\g
          *dataPtr\AmbientColor\b   = *AmbientColor\b
          *dataPtr\AmbientColor\a   = *AmbientColor\a
          *dataPtr\DiffuseColor\r   = *DiffuseColor\r
          *dataPtr\DiffuseColor\g   = *DiffuseColor\g
          *dataPtr\DiffuseColor\b   = *DiffuseColor\b
          *dataPtr\DiffuseColor\a   = *DiffuseColor\a
          *dataPtr\Direction\x      = *Direction\x
          *dataPtr\Direction\y      = *Direction\y
          *dataPtr\Direction\z      = *Direction\z
          *dataPtr\SpecularPower    = *DataDesc\Light\GetSpecularPower()
          *dataPtr\SpecularColor\r  = *SpecularColor\r
          *dataPtr\SpecularColor\g  = *SpecularColor\g
          *dataPtr\SpecularColor\b  = *SpecularColor\b
          *dataPtr\SpecularColor\a  = *SpecularColor\a

          *this\ImmediateContext\Unmap(*this\ConstantBuffer(*this\LightBufferId), 0)
          *this\p\UpdateConstantBuffer(*this\LightBufferId, 1)
       EndIf         
        
    EndSelect
    
  EndProcedure
  
 ; Render the Shader
Procedure Shader_Render(*this.Shader_Template)  
  
  Select *this\Type
    Case 1:            
      Protected *ShaderBlob.ID3D10Blob = *this\Compiler\GetShaderBlob()
        *this\ImmediateContext\IASetInputLayout(*this\Layout)
        *this\ImmediateContext\VSSetShader(*this\vShader, #Null, #Null)
      Case 2:     
        *this\ImmediateContext\PSSetSamplers(0, 1, @*this\SampleState)
        *this\ImmediateContext\PSSetShader(*this\pShader, #Null, #Null)
    EndSelect  
  
EndProcedure 
  
; Release the shader from memory
Procedure Shader_Release(*this.Shader_Template)
  If *this\Compiler         : *this\Compiler\Release()       : EndIf  
  If *this\vShader          : *this\vShader\Release()        : EndIf 
  If *this\pShader          : *this\pShader\Release()        : EndIf 
  If *this\Layout           : *this\Layout\Release()         : EndIf   
  If *this\SampleState      : *this\SampleState\Release()    : EndIf
  
  Protected x.l
  For x = 0 To ArraySize(*this\ConstantBuffer())
    If *this\ConstantBuffer(x) : *this\ConstantBuffer(x)\Release() : EndIf
  Next
  
  FreeArray(*this\ConstantBuffer())
  FreeMemory(*this)  
EndProcedure  
  
;-//    Private procedures    //
Procedure Shader_CreateShader(*this.Shader_Template)
  Protected Result.l = #S_OK  
  Protected *ShaderBlob.ID3D10Blob = *this\Compiler\GetShaderBlob()
  
  Select *this\Type
    Case 1      
      Result = *this\Device\CreateVertexShader(*ShaderBlob\GetBufferPointer(), *ShaderBlob\GetBufferSize(), #Null, @*this\vShader)
      
      ; Create the input layout
      Protected *vShaderReflection.ID3D11ShaderReflection = #Null
      
      Result = D3DReflect( *ShaderBlob\GetBufferPointer(), *ShaderBlob\GetBufferSize(), @IID_ID3D11ShaderReflection,  @*vShaderReflection)
      If Result
        ProcedureReturn Result
      EndIf  
      
      Protected shaderDesc.D3D11_SHADER_DESC 
      *vShaderReflection\GetDesc(@shaderDesc)  
      
      
      Protected Dim iLayout.D3D11_INPUT_ELEMENT_DESC(0)
      Protected x.i      
      
      For x  = 0 To shaderDesc\InputParameters - 1
        
        If x > ArraySize(iLayout()) : ReDim iLayout(x) : EndIf
        
        Protected paramDesc.D3D11_SIGNATURE_PARAMETER_DESC
        
        *vShaderReflection\GetInputParameterDesc(x, @paramDesc)
        
        iLayout(x)\SemanticName          = StringToByteString(PeekS(paramDesc\SemanticName, -1, #PB_Ascii))
        iLayout(x)\SemanticIndex         = paramDesc\SemanticIndex
        iLayout(x)\InputSlot             = 0
        iLayout(x)\AlignedByteOffset     = #D3D11_APPEND_ALIGNED_ELEMENT
        iLayout(x)\InputSlotClass        = #D3D11_INPUT_PER_VERTEX_DATA
        
        iLayout(x)\InstanceDataStepRate  = 0  

        If  paramDesc\Mask = 1 
          
           If       paramDesc\ComponentType = #D3D_REGISTER_COMPONENT_UINT32  :  iLayout(x)\Format = #DXGI_FORMAT_R32_UINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_SINT32  :  iLayout(x)\Format = #DXGI_FORMAT_R32_SINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_FLOAT32 :  iLayout(x)\Format = #DXGI_FORMAT_R32_FLOAT : EndIf
     
        ElseIf paramDesc\Mask <= 3
          
           If       paramDesc\ComponentType = #D3D_REGISTER_COMPONENT_UINT32   :  iLayout(x)\Format = #DXGI_FORMAT_R32G32_UINT 
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_SINT32  :  iLayout(x)\Format = #DXGI_FORMAT_R32G32_SINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_FLOAT32 :  iLayout(x)\Format = #DXGI_FORMAT_R32G32_FLOAT : EndIf     
     
        ElseIf paramDesc\Mask <= 7
         
           If       paramDesc\ComponentType = #D3D_REGISTER_COMPONENT_UINT32   :  iLayout(x)\Format = #DXGI_FORMAT_R32G32B32_UINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_SINT32  :  iLayout(x)\Format = #DXGI_FORMAT_R32G32B32_SINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_FLOAT32 :  iLayout(x)\Format = #DXGI_FORMAT_R32G32B32_FLOAT :  EndIf       
      
        ElseIf paramDesc\Mask <= 15      
          
           If       paramDesc\ComponentType = #D3D_REGISTER_COMPONENT_UINT32   :  iLayout(x)\Format = #DXGI_FORMAT_R32G32B32A32_UINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_SINT32  :  iLayout(x)\Format = #DXGI_FORMAT_R32G32B32A32_SINT
           ElseIf  paramDesc\ComponentType  = #D3D_REGISTER_COMPONENT_FLOAT32 :  iLayout(x)\Format = #DXGI_FORMAT_R32G32B32A32_FLOAT  : EndIf
      
       EndIf  
  
      Next  
      *vShaderReflection\Release()
       
      Result = *this\Device\CreateInputLayout(iLayout(), ArraySize(iLayout()) + 1, *ShaderBlob\GetBufferPointer(), *ShaderBlob\GetBufferSize(), @*this\Layout)     
            
      
      ; Set the VertexShader default ConstantBuffer
      Protected ConstantBufferDesc.D3D11_BUFFER_DESC 
      ConstantBufferDesc\Usage      = #D3D11_USAGE_DEFAULT
      ConstantBufferDesc\ByteWidth  = SizeOf(D3DMATRIX3) 
      ConstantBufferDesc\BindFlags  = #D3D11_BIND_CONSTANT_BUFFER
      *this\DefBufferId = *this\p\AddConstantBuffer(@ConstantBufferDesc)          
      
    Case 2  
      Result = *this\Device\CreatePixelShader(*ShaderBlob\GetBufferPointer(), *ShaderBlob\GetBufferSize(), #Null, @*this\pShader)     
  EndSelect
  ProcedureReturn Result
EndProcedure

;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;-//    DataSection    //
DataSection
  Shader_Class:    
  Data.i @Shader_AddTexture()
  Data.i @Shader_AddLighting()  
  Data.i @Shader_CompileShaderFile()
  Data.i @Shader_AddConstantBuffer()
  Data.i @Shader_UpdateConstantBuffer()
  Data.i @Shader_SetShaderData()
  Data.i @Shader_Render()
  Data.i @Shader_Release()
EndDataSection 



; IDE Options = PureBasic 5.20 beta 14 LTS (Windows - x64)
; CursorPosition = 259
; FirstLine = 254
; Folding = --
; EnableXP
; EnableCompileCount = 0
; EnableBuildCount = 0